package com.nightpiter.controller.reflection;

import java.util.ArrayList;
import java.util.Hashtable;

import com.nightpiter.model.exceptions.BindingBuilderException;

/**
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 */
public class PackBuilder {

	/**
	 * temporary structure class to store raw data about Class XML element
	 */
	private class ClassStruct {
		public int id;
		public String name;
	}

	/**
	 * temporary structure class to store raw data about Package XML element
	 */
	private class ModuleStruct {
		public String name = "", prefix = "";
		public Hashtable<Integer, String> classHash = new Hashtable<Integer, String>();
		public ArrayList<String> authors = new ArrayList<String>();
		public int mainClass;
	}

	/** PackageManifest object to build */
	private Pack pack;

	/** temporary structure to write package data in */
	private ModuleStruct moduleStruct;

	/** temporary structure to write class data in */
	private ClassStruct classStruct;

	/** Collection of Package temporary structures */
	private ArrayList<ModuleStruct> moduleStructList = new ArrayList<ModuleStruct>();

	/** the name of the pack */
	private String packName = "";

	/** comment to the map */
	private String comment = "";

	/** authors' names */
	private ArrayList<String> authors = new ArrayList<String>();

	/**
	 * public constructor
	 * 
	 * @param pack
	 */
	public PackBuilder(Pack pack) {
		this.pack = pack;
	}

	/**
	 * performs bind-by-id resolving and passes the result to the model's build
	 * method
	 * 
	 * @throws BindingBuilderException
	 */
	public void build() throws BindingBuilderException {
		for (ModuleStruct packStruct : this.moduleStructList) {
			String mainClassName = packStruct.classHash
					.get(packStruct.mainClass);
			if (mainClassName == null) {
				throw new BindingBuilderException(
						"There is no class element with such id",
						packStruct.mainClass, "main", "classes");
			}
			ArrayList<String> classNames = new ArrayList<String>(
					packStruct.classHash.values());
			new Module(pack, packStruct.name, packStruct.prefix, classNames,
					mainClassName, packStruct.authors);
		}
		pack.build(packName, comment, authors);
	}

	/**
	 * called when the name of the pack has retrieved
	 * 
	 * @param packName
	 */
	public void setPackName(String packName) {
		this.packName = packName;
	}

	/**
	 * called when the author name retrieved
	 * 
	 * @param authorName
	 */
	public void setPackAuthorName(String authorName) {
		this.authors.add(authorName);
	}

	public void setNote(String note) {
		this.comment = note;
	}

	/**
	 * called when a new package element has occured
	 */
	public void startModule() {
		this.moduleStruct = new ModuleStruct();
	}

	/**
	 * sets the new package's name
	 * 
	 * @param name
	 */
	public void setModuleName(String name) {
		this.moduleStruct.name = name;
	}

	/**
	 * sets the new package's prefix
	 * 
	 * @param prefix
	 */
	public void setModulePrefix(String prefix) {
		this.moduleStruct.prefix = prefix;
	}

	public void setModuleAuthorName(String authorName) {
		this.moduleStruct.authors.add(authorName);
	}

	/**
	 * adds the packageStruct to the list
	 */
	public void endModule() {
		this.moduleStructList.add(this.moduleStruct);
	}

	/**
	 * called when a new class element has occured and it's id's retrieved
	 * 
	 * @param id
	 */
	public void startClass(int id) {
		this.classStruct = new ClassStruct();
		this.classStruct.id = id;
	}

	/**
	 * sets the new class's name
	 * 
	 * @param name
	 */
	public void setClassName(String name) {
		this.classStruct.name = name;
	}

	/**
	 * instantiates Class with retrieved parameters
	 */
	public void endClass() {
		this.moduleStruct.classHash.put(classStruct.id, classStruct.name);
	}

	/**
	 * called when entering classes block
	 */
	public void startClasses(int main) {
		moduleStruct.mainClass = main;
	}

	/**
	 * called when exiting classes block
	 */
	public void endClasses() {
	}

}